package jp.dodododo.aop.impl;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javassist.CtBehavior;
import jp.dodododo.aop.Aspect;
import jp.dodododo.aop.Def;
import jp.dodododo.aop.annotation.Enhance;
import jp.dodododo.aop.xml.XmlElement;

@Enhance(false)
public class DefImpl implements Def, XmlElement {

	private List<Aspect> aspects = new ArrayList<Aspect>();

	private String targetClassPattern;

	private int lineNumber;

	public DefImpl(String targetClassPattern, int lineNumber) {
		this.targetClassPattern = targetClassPattern;
		this.lineNumber = lineNumber;
	}

	public List<Aspect> getAspects() {
		return aspects;
	}

	public void add(Aspect aspect) {
		aspects.add(aspect);
	}

	public String[] getApplyInterceptorStatements(CtBehavior methodOrConstructor) {
		List<String> ret = new ArrayList<String>();
		for (Iterator<Aspect> iter = aspects.iterator(); iter.hasNext();) {
			Aspect aspect = iter.next();
			if (isAppliedModifiers(methodOrConstructor, aspect) == true && aspect.isApplied(methodOrConstructor) == true) {
				ret.add(aspect.getInterceptorStatement());
			}
		}
		return ret.toArray(new String[ret.size()]);
	}

	private boolean isAppliedModifiers(CtBehavior methodOrConstructor, Aspect aspect) {
		int methodModifiers = methodOrConstructor.getModifiers();
		int accessModifier = aspect.getAccessModifier();
		if (Modifier.isPrivate(accessModifier) == true) {
			return true;
		} else if (Modifier.isProtected(accessModifier)) {
			return Modifier.isProtected(methodModifiers) == true || Modifier.isPublic(methodModifiers);
		} else if (Modifier.isPublic(accessModifier)) {
			return Modifier.isPublic(methodModifiers);
		} else {
			return Modifier.isPrivate(methodModifiers) == false;
		}
	}

	public String getTargetClassPattern() {
		return targetClassPattern;
	}

	public int getLineNumber() {
		return lineNumber;
	}

	@Override
	public boolean equals(Object o) {
		try {
			DefImpl target = (DefImpl) o;
			return this.aspects.equals(target.aspects) && this.targetClassPattern.equals(target.targetClassPattern)
					&& this.lineNumber == target.lineNumber;
		} catch (ClassCastException e) {
			return false;
		}
	}

	@Override
	public int hashCode() {
		if (targetClassPattern == null) {
			return lineNumber;
		}
		return targetClassPattern.hashCode();
	}
}
